package rm.metrology.server;

import java.util.List;

import rm.metrology.shared.LocationEnum;
import rm.metrology.shared.MeasurementEntity;
import rm.metrology.shared.MetroEntity;
import rm.metrology.shared.StatusEnum;
import rm.metrology.shared.UserEntity;

public class MetroDAO extends BaseDAO {

	
	private static final MetroDAO fInstance = new MetroDAO();
	
	public static final MetroDAO getInstance() {
		return fInstance;
	}
	

	private final static String[] STATUS_ACT = { 
			StatusEnum.NEW.toString(),
			StatusEnum.CALIBRATING.toString(),
			StatusEnum.CALIBRATED.toString(), 
			StatusEnum.PAYING.toString() 
	};

	private final static String[] STATUS_REJ = {
			StatusEnum.WASTED.toString(), 
			StatusEnum.INACCURATE.toString(),
			StatusEnum.UNPAID.toString(), 
			StatusEnum.EXPIRED.toString(),
			StatusEnum.VOID.toString() 
	};

	private final static String[] STATUS_REG = {
			StatusEnum.REGISTERED.toString() 
	};

	
	private MetroDAO()  {
	}
	
	
	public MetroEntity insert(MetroEntity e) {
		validate(e);
		verifyAccess(e.getLocationType());
		e.doInsert(getOperator());
		ofy().put(e);
		return e;
	}
	
	public MetroEntity update(Long id, MetroEntity e) {
		validate(e);
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doUpdate(getOperator(), e);
		ofy().put(eDB);
		return eDB;		
	}
	
	public MetroEntity equipmentFail(Long id, String reason) {
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doEquipmentFail(getOperator(), reason);
		ofy().put(eDB);
		return eDB;		
	}
	
	public MetroEntity equipmentPass(Long id) {
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doEquipmentPass(getOperator());
		ofy().put(eDB);
		return eDB;		
	}
	
	public MetroEntity measureAdd(Long id, MeasurementEntity e) {
		validate(e);
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doMeasurementAdd(getOperator(), e);
		ofy().put(eDB);
		return eDB;
	}
	
	public MetroEntity measureRemove(Long id, Long measureCreatedAt) {
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doMeasurementRemove(getOperator(), measureCreatedAt);
		ofy().put(eDB);
		return eDB;
	}
	
	public MetroEntity measuresEvaluate(Long id) {
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doMeasurementsEvaluate(getOperator());
		ofy().put(eDB);
		return eDB;				
	}
	
	public MetroEntity paid(Long id, String receipt) {
		MetroEntity eDB = ofy().get(MetroEntity.class, id);
		verifyAccess(eDB.getLocationType());
		eDB.doPayment(getOperator(), receipt);
		ofy().put(eDB);
		return eDB;
	}
	
	public List<MetroEntity> selectActive() {
		return ofy().query(MetroEntity.class).filter("fStatus in ", STATUS_ACT).list();
	}

	public List<MetroEntity> selectRejected() {
		return ofy().query(MetroEntity.class).filter("fStatus in ", STATUS_REJ).list();
	}

	public List<MetroEntity> selectRegistered() {
		return ofy().query(MetroEntity.class).filter("fStatus", STATUS_REG).list();
	}

	private String verifyAccess(LocationEnum metroLoc) {
		UserEntity user = UserDAO.getInstance().getCurrent();
		LocationEnum userLoc = user.getLocationType();
		if (userLoc != LocationEnum.NATIONAL && userLoc != metroLoc) {
			String msg = "ONLY users from : " + LocationEnum.NATIONAL.toString() 
					+ ", " + metroLoc.toString();
			throw new IllegalStateException(msg);
		}
		return user.getLocationType().toString() + ", "
				+ user.getStaff().toLowerCase() + ": ";
	}

}
